home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
programr
/
ole2book.zip
/
CHAP06.ZIP
/
CHAP06
/
FREELOAD
/
DOCUMENT.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1993-05-21
|
17KB
|
658 lines
/*
* DOCUMENT.CPP
*
* Implementation of the CFreeloaderDoc derivation of CDocument.
* We create a default handler object and use it for drawing, data
* caching, and serialization.
*
* Copyright (c)1993 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Software Design Engineer
* Microsoft Systems Developer Relations
*
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/
#include "freeload.h"
/*
* CFreeloaderDoc::CFreeloaderDoc
* CFreeloaderDoc::~CFreeloaderDoc
*
* Constructor Parameters:
* hInst HINSTANCE of the application.
*/
CFreeloaderDoc::CFreeloaderDoc(HINSTANCE hInst)
: CDocument(hInst)
{
m_pIStorage=NULL;
m_pIUnknown=NULL;
m_dwConn=0;
return;
}
CFreeloaderDoc::~CFreeloaderDoc(void)
{
ReleaseObject();
if (NULL!=m_pIStorage)
m_pIStorage->Release();
return;
}
/*
* CFreeloaderDoc::ReleaseObject
*
* Purpose:
* Centralizes cleanup code for the object and its cache.
*
* Parameters:
* None
*
* Return Value:
* None
*/
void CFreeloaderDoc::ReleaseObject(void)
{
LPOLECACHE pIOleCache;
HRESULT hr;
if (0!=m_dwConn)
{
hr=m_pIUnknown->QueryInterface(IID_IOleCache
, (LPVOID FAR *)&pIOleCache);
if (SUCCEEDED(hr))
{
pIOleCache->Uncache(m_dwConn);
pIOleCache->Release();
}
}
if (NULL!=m_pIUnknown)
m_pIUnknown->Release();
CoFreeUnusedLibraries();
m_dwConn=0;
m_pIUnknown=NULL;
return;
}
/*
* CFreeloaderDoc::FMessageHook
*
* Purpose:
* Processes WM_PAINT for the document so we can draw the object.
*
* Parameters:
* <WndProc Parameters>
* pLRes LRESULT FAR * in which to store the return value
* for the message.
*
* Return Value:
* BOOL TRUE to prevent further processing, FALSE otherwise.
*/
BOOL CFreeloaderDoc::FMessageHook(HWND hWnd, UINT iMsg, WPARAM wParam
, LPARAM lParam, LRESULT FAR *pLRes)
{
PAINTSTRUCT ps;
HDC hDC;
RECT rc;
RECTL rcl;
LPVIEWOBJECT pIViewObject;
HRESULT hr;
if (WM_PAINT!=iMsg)
return FALSE;
hDC=BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rc);
/*
* To draw the object we can either QueryInterface for an IViewObject,
* call IViewObject::Draw, and IViewObject::Release, or we can use
* OleDraw which does exactly the same three steps, only calling
* ::Draw with defaults. OleDraw does exactly what is done here.
*/
if (NULL!=m_pIUnknown)
{
hr=m_pIUnknown->QueryInterface(IID_IViewObject
, (LPVOID FAR *)&pIViewObject);
if (SUCCEEDED(hr))
{
#ifndef WIN32
rcl.left =MAKELONG(rc.left, 0);
rcl.right =MAKELONG(rc.right, 0);
rcl.top =MAKELONG(rc.top, 0);
rcl.bottom=MAKELONG(rc.bottom, 0);
#else
//Win32 rectangles are already LONGs.
rcl=rc;
#endif
pIViewObject->Draw(DVASPECT_CONTENT, -1, NULL, NULL, 0, hDC
, &rcl, NULL, NULL, 0);
pIViewObject->Release();
}
}
EndPaint(hWnd, &ps);
return FALSE;
}
/*
* CFreeloaderDoc::ULoad
*
* Purpose:
* Loads a given document without any user interface overwriting the
* previous contents of the Polyline window. We do this by opening
* the file and telling the Polyline to load itself from that file.
*
* Parameters:
* fChangeFile BOOL indicating if we're to update the window title
* and the filename from using this file.
* pszFile LPSTR to the filename to load, NULL if the file is
* new and untitled.
*
* Return Value:
* UINT An error value from DOCERR_*
*/
UINT CFreeloaderDoc::ULoad(BOOL fChangeFile, LPSTR pszFile)
{
HRESULT hr;
CLSID clsID;
LPSTORAGE pIStorage;
LPUNKNOWN pIUnknown;
LPPERSISTSTORAGE pIPersistStorage;
DWORD dwMode=STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
if (NULL==pszFile)
{
//Create a new temp file.
hr=StgCreateDocfile(NULL, dwMode | STGM_CREATE | STGM_DELETEONRELEASE
, 0, &pIStorage);
if (FAILED(hr))
return DOCERR_COULDNOTOPEN;
m_pIStorage=pIStorage;
FDirtySet(FALSE);
Rename(NULL);
return DOCERR_NONE;
}
//Attempt to open the storage.
hr=StgOpenStorage(pszFile, NULL, dwMode, NULL, 0, &pIStorage);
if (FAILED(hr))
return DOCERR_COULDNOTOPEN;
/*
* When we previously called IPersistStorage::Save, OLE2.DLL kindly
* placed a CLSID into the IStorage for us, either CLSID_StaticMetafile,
* known to us as CLSID_FreeMetafile, or CLSID_StaticDib, known to us
* as CLSID_FreeDib. All we have to do is load this CLSID,
* create the object for that ID (using CoCreateInstance, see ::FPaste
* below).
*/
hr=ReadClassStg(pIStorage, &clsID);
//See if we know about it.
if (FAILED(hr) || !(IsEqualCLSID(clsID, CLSID_FreeMetafile)
|| IsEqualCLSID(clsID, CLSID_FreeDib)))
{
pIStorage->Release();
return DOCERR_READFAILURE;
}
//Go create an object, then tell *it* to load the data.
hr=CoCreateInstance(clsID, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown
, (LPVOID FAR *)&pIUnknown);
if (FAILED(hr))
{
pIStorage->Release();
return DOCERR_READFAILURE;
}
//Get IPersistStorage for the data we hold.
pIUnknown->QueryInterface(IID_IPersistStorage, (LPVOID FAR *)&pIPersistStorage);
hr=pIPersistStorage->Load(pIStorage);
pIPersistStorage->Release();
if (FAILED(hr))
{
pIUnknown->Release();
pIStorage->Release();
return DOCERR_READFAILURE;
}
m_pIStorage=pIStorage;
m_pIUnknown=pIUnknown;
Rename(pszFile);
FDirtySet(FALSE);
return DOCERR_NONE;
}
/*
* CFreeloaderDoc::USave
*
* Purpose:
* Writes the file to a known filename, requiring that the user has
* previously used FileOpen or FileSaveAs in order to have a filename.
*
* Parameters:
* uType UINT indicating the type of file the user requested
* to save in the File Save As dialog.
* pszFile LPSTR under which to save. If NULL, use the current name.
*
* Return Value:
* UINT An error value from DOCERR_*
*/
UINT CFreeloaderDoc::USave(UINT uType, LPSTR pszFile)
{
HRESULT hr;
LPSTORAGE pIStorage;
LPPERSISTSTORAGE pIPersistStorage;
CLSID clsID;
//If we have no data object, there's nothing to save.
if (NULL==m_pIUnknown)
return DOCERR_WRITEFAILURE;
//Get IPersistStorage for the data we hold.
hr=m_pIUnknown->QueryInterface(IID_IPersistStorage, (LPVOID FAR *)&pIPersistStorage);
if (FAILED(hr))
return DOCERR_WRITEFAILURE;
//Save or Save As with the same file is just a commit.
if (NULL==pszFile || (NULL!=pszFile && 0==lstrcmpi(pszFile, m_szFile)))
{
pIPersistStorage->Save(m_pIStorage, TRUE);
m_pIStorage->Commit(STGC_ONLYIFCURRENT);
pIPersistStorage->SaveCompleted(m_pIStorage);
pIPersistStorage->Release();
FDirtySet(FALSE);
return DOCERR_NONE;
}
/*
* When we're given a name, open the storage, creating it new if
* it does not exist or overwriting the old one. Then ::CopyTo
* from the current to the new, ::Commit the new, then ::Release
* the old.
*/
hr=StgCreateDocfile(pszFile, STGM_TRANSACTED | STGM_READWRITE
| STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, &pIStorage);
if (FAILED(hr))
return DOCERR_COULDNOTOPEN;
//Insure the image is up to date, then tell it we're changing
pIPersistStorage->Save(m_pIStorage, TRUE);
pIPersistStorage->HandsOffStorage();
//Save the class, bitmap or metafile
pIPersistStorage->GetClassID(&clsID);
hr=WriteClassStg(m_pIStorage, clsID);
hr=m_pIStorage->CopyTo(NULL, NULL, NULL, pIStorage);
if (FAILED(hr))
{
pIPersistStorage->SaveCompleted(m_pIStorage);
pIPersistStorage->Release();
pIStorage->Release();
return DOCERR_WRITEFAILURE;
}
pIStorage->Commit(STGC_ONLYIFCURRENT);
/*
* Revert changes on the original storage. If this was a temp file,
* it's deleted since we used STGM_DELETEONRELEASE.
*/
m_pIStorage->Release();
//Make this new storage current
m_pIStorage=pIStorage;
pIPersistStorage->SaveCompleted(m_pIStorage);
pIPersistStorage->Release();
Rename(pszFile);
FDirtySet(FALSE);
return DOCERR_NONE;
}
/*
* CFreeloaderDoc::FClip
*
* Purpose:
* Places a private format, a metafile, and a bitmap of the display
* on the clipboard, optionally implementing Cut by deleting the
* data in the current window after rendering.
*
* Parameters:
* hWndFrame HWND of the main window.
* fCut BOOL indicating cut (TRUE) or copy (FALSE).
*
* Return Value:
* BOOL TRUE if successful, FALSE otherwise.
*/
BOOL CFreeloaderDoc::FClip(HWND hWndFrame, BOOL fCut)
{
BOOL fRet=TRUE;
static UINT rgcf[3]={CF_METAFILEPICT, CF_DIB, CF_BITMAP};
const UINT cFormats=3;
UINT i;
HGLOBAL hMem;
if (NULL==m_pIUnknown)
return FALSE;
if (!OpenClipboard(hWndFrame))
return FALSE;
//Clean out whatever junk is in the clipboard.
EmptyClipboard();
for (i=0; i < cFormats; i++)
{
hMem=RenderFormat(rgcf[i]);
if (NULL!=hMem)
{
SetClipboardData(rgcf[i], hMem);
fRet=TRUE;
break;
}
}
//Free clipboard ownership.
CloseClipboard();
//If we're cutting, clean out the cache and the object we hold.
if (fRet && fCut)
{
ReleaseObject();
InvalidateRect(m_hWnd, NULL, TRUE);
UpdateWindow(m_hWnd);
FDirtySet(TRUE);
}
return fRet;
}
/*
* CFreeloaderDoc::RenderFormat
*
* Purpose:
* Renders a specific clipboard format into global memory. We have this
* function split out because we'll eventually move to delayed rendering
* and this will then be immediately callable from the frame.
*
* Parameters:
* cf UINT format to render.
*
* Return Value:
* HGLOBAL Global memory handle containing the data.
*/
HGLOBAL CFreeloaderDoc::RenderFormat(UINT cf)
{
LPDATAOBJECT pIDataObject;
FORMATETC fe;
STGMEDIUM stm;
if (NULL==m_pIUnknown)
return NULL;
//We only have to ask the data object (cache) for the data.
switch (cf)
{
case CF_METAFILEPICT:
stm.tymed=TYMED_MFPICT;
break;
case CF_DIB:
stm.tymed=TYMED_HGLOBAL;
break;
case CF_BITMAP:
stm.tymed=TYMED_GDI;
break;
default:
return NULL;
}
stm.hGlobal=NULL;
SETFormatEtc(fe, cf, DVASPECT_CONTENT, NULL, stm.tymed, -1);
m_pIUnknown->QueryInterface(IID_IDataObject, (LPVOID FAR *)&pIDataObject);
pIDataObject->GetData(&fe, &stm);
pIDataObject->Release();
return stm.hGlobal;
}
/*
* CFreeloaderDoc::FQueryPaste
*
* Purpose:
* Determines if we can paste data from the clipboard.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if data is available, FALSE otherwise.
*/
BOOL CFreeloaderDoc::FQueryPaste(void)
{
return IsClipboardFormatAvailable(CF_BITMAP)
|| IsClipboardFormatAvailable(CF_DIB)
|| IsClipboardFormatAvailable(CF_METAFILEPICT);
}
/*
* CFreeloaderDoc::FPaste
*
* Purpose:
* Retrieves the private data format from the clipboard and sets it
* to the current figure in the editor window.
*
* Note that if this function is called, then the clipboard format
* is available because the Paste menu item is only enabled if the
* format is present.
*
* Parameters:
* hWndFrame HWND of the main window.
*
* Return Value:
* BOOL TRUE if successful, FALSE otherwise.
*/
BOOL CFreeloaderDoc::FPaste(HWND hWndFrame)
{
UINT cf=0;
BOOL fRet=FALSE;
HRESULT hr;
DWORD dwConn;
LPUNKNOWN pIUnknown;
LPOLECACHE pIOleCache;
LPPERSISTSTORAGE pIPersistStorage;
FORMATETC fe;
STGMEDIUM stm;
CLSID clsID;
if (!OpenClipboard(hWndFrame))
return FALSE;
/*
* Try to get data in order of metafile, dib, bitmap. We set stm.tymed
* up front so if we actually get something a call to ReleaseStgMedium
* will clean it up for us.
*/
stm.tymed=TYMED_MFPICT;
stm.hGlobal=GetClipboardData(CF_METAFILEPICT);
if (NULL!=stm.hGlobal)
cf=CF_METAFILEPICT;
if (0==cf)
{
stm.tymed=TYMED_HGLOBAL;
stm.hGlobal=GetClipboardData(CF_DIB);
if (NULL!=stm.hGlobal)
cf=CF_DIB;
}
if (0==cf)
{
stm.tymed=TYMED_GDI;
stm.hGlobal=GetClipboardData(CF_BITMAP);
if (NULL!=stm.hGlobal)
cf=CF_BITMAP;
}
CloseClipboard();
//Didn't get anything? Then we're finished.
if (0==cf)
return FALSE;
//This now describes the data we have.
SETFormatEtc(fe, cf, DVASPECT_CONTENT, NULL, stm.tymed, -1);
/*
* Create an object to deal with this data. There's two ways to
* do this: CoCreateInstance (CoGetClassObject) or
* OleCreateDefaultHandler. The first will go through all the exercises
* of looking up the CLSID in the regDB, finding OLE2.DLL (registered
* for these classes), getting a class factory, and using
* IClassFactory::CreateInstance.
*
* The second method directly into ole2.dll which creates an object
* identical to that created with CoCreateInstnace. This call is more
* terse and generally faster.
*
* This function always uses OleCreateDefaultHandler. For an
* example of CoCreateInstance, see ::ULoad above.
*/
if (CF_METAFILEPICT==cf)
clsID=CLSID_FreeMetafile;
else
clsID=CLSID_FreeDib;
hr=OleCreateDefaultHandler(clsID, NULL, IID_IUnknown, (LPVOID FAR *)&pIUnknown);
if (FAILED(hr))
{
ReleaseStgMedium(&stm);
return FALSE;
}
/*
* Our contract says we provide storage through IPersistStorage::InitNew.
* We know that the object we're dealing with supports IPersistStorage
* and IOleCache, so we don't bother to check return values.
* HACK: Living Dangerously?
*/
pIUnknown->QueryInterface(IID_IPersistStorage, (LPVOID FAR *)&pIPersistStorage);
pIPersistStorage->InitNew(m_pIStorage);
pIPersistStorage->Release();
//Now that we have the cache object, shove the data into it.
pIUnknown->QueryInterface(IID_IOleCache, (LPVOID FAR *)&pIOleCache);
pIOleCache->Cache(&fe, ADVF_PRIMEFIRST, &dwConn);
hr=pIOleCache->SetData(&fe, &stm, TRUE);
pIOleCache->Release();
if (FAILED(hr))
{
ReleaseStgMedium(&stm);
pIUnknown->Release();
return FALSE;
}
//Now that that's all done, replace our current with the new.
ReleaseObject();
m_pIUnknown=pIUnknown;
m_dwConn=dwConn;
FDirtySet(TRUE);
InvalidateRect(m_hWnd, NULL, TRUE);
UpdateWindow(m_hWnd);
return TRUE;
}